//
// Created by LiuYou on 2021/5/1.
//
#define _GNU_SOURCE 1

//#include <cygwin/in.h>
#include <fcntl.h>
#include <iostream>
#include <libgen.h>
#include <arpa/inet.h>
#include <cassert>
#include <sys/poll.h>
#include <cstring>
#include <sys/unistd.h>

inline constexpr int getBufferSize() {
    return 64;
}

struct ClientData {
    sockaddr_in sockAddrIn;
    char *writeBuffer;
    char buffer[getBufferSize()];
};

int setNonBlocking(int fd) {
    int oldOption = fcntl(fd, F_GETFL);
    int newOption = oldOption | O_NONBLOCK;
    fcntl(fd, F_SETFL, newOption);
    return oldOption;
}


int main(int argc, char **argv) {
    if (argc <= 2) {
        std::cout << "usage: " << basename(argv[0]) << " IPAddress PortNumber" << std::endl;
        return 1;
    }
    const char *ip = argv[1];
    int port = std::atoi(argv[2]);

    sockaddr_in sockAddrIn{
            .sin_family=PF_INET,
            .sin_port=htons(port),
            {.s_addr=0}
    };
    inet_pton(AF_INET, ip, &sockAddrIn.sin_addr);

    int listenFD = socket(PF_INET, SOCK_STREAM, 0);
    assert(listenFD >= 0);

    int ret = bind(listenFD, (sockaddr *) &sockAddrIn, sizeof(sockAddrIn));
    assert(ret != -1);

    ret = listen(listenFD, 5);
    assert(ret != -1);

    /*
     * 创建users数组, 分配FD_LIMIT个ClientData对象。
     * 可以预测: 每个可能的socket连接都可以获得一个这样的对象, 并且socket的值可以直接用来索引(作为数组下标)socket连接对应的ClientData对象,
     * 这是将socket和客户数据关联的简单而高效的方式。
     */
    int FD_LIMIT = 65535;
    ClientData *clientData = new ClientData[FD_LIMIT];
    // 尽管我们分配了足够多的ClientData对象, 但为了提高poll的性能, 仍然有必要限制用户的数量。
    int USER_LIMIT = 5;
    pollfd fds[USER_LIMIT + 1];
    for (int i = 1; i < USER_LIMIT; ++i) {
        fds[i].fd = -1;
        fds[i].events = 0;
    }
    fds[0].fd = listenFD;
    fds[0].events = POLLIN | POLLERR;
    fds[0].revents = 0;
    int userCounter = 0;
    for (;;) {
        ret = poll(fds, userCounter + 1, -1);
        if (ret < 0) {
            std::printf("poll failure\n");
            break;
        }
        for (int i = 0; i < userCounter + 1; ++i) {
            if ((fds[i].fd == listenFD) && (fds[i].revents & POLLIN)) {
                sockaddr_in clientSockAddrIn;
                socklen_t clientSockAddrInLength = sizeof(clientSockAddrIn);
                int connectFD = accept(listenFD, (sockaddr *) &clientSockAddrIn, &clientSockAddrInLength);
                if (connectFD < 0) {
                    std::printf("errno is: %d\n", errno);
                    continue;
                }
                // 如果请求太多, 则关闭新到的连接
                if (userCounter >= USER_LIMIT) {
                    const char *info = "too many users\n";
                    std::printf("%s", info);
                    send(connectFD, info, std::strlen(info), 0);
                    close(connectFD);
                    continue;
                }
                /*
                 * 对于新的连接, 同时修改fds和clientData数组。前文已经提到, clientData[connectFD]对应于
                 * 新连接文件描述符connectFD的客户数据
                 */
                ++userCounter;
                clientData[connectFD].sockAddrIn = clientSockAddrIn;
                setNonBlocking(connectFD);
                fds[userCounter].fd = connectFD;
                fds[userCounter].events = POLLIN | POLLRDHUP | POLLERR;
                fds[userCounter].revents = 0;
                std::printf("comes a new user, now have %d users\n", userCounter);
            } else if (fds[i].revents & POLLERR) {
                std::printf("get an error from %d\n", fds[i].fd);
                char errors[100];
                std::memset(errors, '\0', 100);
                socklen_t length = sizeof(errors);
                if (getsockopt(fds[i].fd, SOL_SOCKET, SO_ERROR, &errors, &length) < 0) {
                    std::printf("get socket option failed\n");
                }
                continue;
            } else if (fds[i].revents & POLLRDHUP) {
                // 如果客户端关闭连接, 则服务器也关闭对应的连接, 并将用户总数减1
                clientData[fds[i].fd] = clientData[fds[userCounter].fd];
                close(fds[i].fd);
                fds[i] = fds[userCounter];
                --i;
                --userCounter;
                std::printf("a client left\n");
            } else if (fds[i].revents & POLLIN) {
                int connectFD = fds[i].fd;
                std::memset(clientData[connectFD].buffer, '\0', getBufferSize());
                ret = recv(connectFD, clientData[connectFD].buffer, getBufferSize() - 1, 0);
                std::printf("get %d bytes of client data %s from %d\n", ret, clientData[connectFD].buffer, connectFD);
                if (ret < 0) {
                    // 如果读操作出错, 则关闭连接
                    if (errno != EAGAIN) {
                        close(connectFD);
                        clientData[fds[i].fd] = clientData[fds[userCounter].fd];
                        fds[i] = fds[userCounter];
                        --i;
                        --userCounter;
                    }
                } else if (ret == 0) {

                } else {
                    for (int j = 0; j <= userCounter; ++j) {
                        if (fds[j].fd == connectFD) {
                            continue;
                        }
                        fds[j].events |= ~POLLIN;
                        fds[j].events |= POLLOUT;
                        clientData[fds[j].fd].writeBuffer = clientData[connectFD].buffer;
                    }
                }
            } else if (fds[i].revents & POLLOUT) {
                int connectFD = fds[i].fd;
                if (!clientData[connectFD].writeBuffer) {
                    continue;
                }
                ret = send(connectFD, clientData[connectFD].writeBuffer, std::strlen(clientData[connectFD].writeBuffer),
                           0);
                clientData[connectFD].writeBuffer = NULL;
                // 写完数据后需要重新注册fds[i]上的可读事件
                fds[i].events |= ~POLLOUT;
                fds[i].events |= POLLIN;
            }
        }
    }
    delete[] clientData;
    close(listenFD);

    return 0;
}








// //
// // Created by LiuYou on 2021/5/1.
// //
// #define _GNU_SOURCE 1

// //#include <cygwin/in.h>
// #include <fcntl.h>
// #include <iostream>
// #include <libgen.h>
// #include <arpa/inet.h>
// #include <cassert>
// #include <sys/poll.h>
// #include <cstring>
// #include <sys/unistd.h>

// inline constexpr int getBufferSize() {
//     return 64;
// }

// struct ClientData {
//     sockaddr_in sockAddrIn;
//     char *writeBuffer;
//     char buffer[getBufferSize()];
// };

// int setNonBlocking(int fd) {
//     int oldOption = fcntl(fd, F_GETFL);
//     int newOption = oldOption | O_NONBLOCK;
//     fcntl(fd, F_SETFL, newOption);
//     return oldOption;
// }


// int main(int argc, char **argv) {
//     if (argc <= 2) {
//         std::cout << "usage: " << basename(argv[0]) << " IPAddress PortNumber" << std::endl;
//         return 1;
//     }
//     const char *ip = argv[1];
//     int port = std::atoi(argv[2]);

//     sockaddr_in sockAddrIn{
//             .sin_family=PF_INET,
//             .sin_port=htons(port),
//             {.s_addr=0}
//     };
//     inet_pton(AF_INET, ip, &sockAddrIn.sin_addr);

//     int listenFD = socket(PF_INET, SOCK_STREAM, 0);
//     assert(listenFD >= 0);

//     int ret = bind(listenFD, (sockaddr *) &sockAddrIn, sizeof(sockAddrIn));
//     assert(ret != -1);

//     ret = listen(listenFD, 5);
//     assert(ret != -1);

//     /*
//      * 创建users数组, 分配FD_LIMIT个ClientData对象。
//      * 可以预测: 每个可能的socket连接都可以获得一个这样的对象, 并且socket的值可以直接用来索引(作为数组下标)socket连接对应的ClientData对象,
//      * 这是将socket和客户数据关联的简单而高效的方式。
//      */
//     int FD_LIMIT = 65535;
//     ClientData *clientData = new ClientData[FD_LIMIT];
//     // 尽管我们分配了足够多的ClientData对象, 但为了提高poll的性能, 仍然有必要限制用户的数量。
//     int USER_LIMIT = 5;
//     pollfd fds[USER_LIMIT + 1];
//     for (int i = 1; i < USER_LIMIT; ++i) {
//         fds[i].fd = -1;
//         fds[i].events = 0;
//     }
//     fds[0].fd = listenFD;
//     fds[0].events = POLLIN | POLLERR;
//     fds[0].revents = 0;
//     int userCounter = 0;
//     for (;;) {
//         ret = poll(fds, userCounter + 1, -1);
//         if (ret < 0) {
//             std::printf("poll failure\n");
//             break;
//         }
//         for (int i = 0; i < userCounter + 1; ++i) {
//             if ((fds[i].fd == listenFD) && (fds[i].revents & POLLIN)) {
//                 sockaddr_in clientSockAddrIn;
//                 socklen_t clientSockAddrInLength = sizeof(clientSockAddrIn);
//                 int connectFD = accept(listenFD, (sockaddr *) &clientSockAddrIn, &clientSockAddrInLength);
//                 if (connectFD < 0) {
//                     std::printf("errno is: %d\n", errno);
//                     continue;
//                 }
//                 // 如果请求太多, 则关闭新到的连接
//                 if (userCounter >= USER_LIMIT) {
//                     const char *info = "too many users\n";
//                     std::printf("%s", info);
//                     send(connectFD, info, std::strlen(info), 0);
//                     close(connectFD);
//                     continue;
//                 }
//                 /*
//                  * 对于新的连接, 同时修改fds和clientData数组。前文已经提到, clientData[connectFD]对应于
//                  * 新连接文件描述符connectFD的客户数据
//                  */
//                 ++userCounter;
//                 clientData[connectFD].sockAddrIn = clientSockAddrIn;
//                 setNonBlocking(connectFD);
//                 fds[userCounter].fd = connectFD;
//                 fds[userCounter].events = POLLIN | POLLRDHUP | POLLERR;
//                 fds[userCounter].revents = 0;
//                 std::printf("comes a new user, now have %d users\n", userCounter);
//             } else if (fds[i].revents & POLLERR) {
//                 std::printf("get an error from %d\n", fds[i].fd);
//                 char errors[100];
//                 std::memset(errors, '\0', 100);
//                 socklen_t length = sizeof(errors);
//                 if (getsockopt(fds[i].fd, SOL_SOCKET, SO_ERROR, &errors, &length) < 0) {
//                     std::printf("get socket option failed\n");
//                 }
//                 continue;
//             } else if (fds[i].revents & POLLRDHUP) {
//                 // 如果客户端关闭连接, 则服务器也关闭对应的连接, 并将用户总数减1
//                 clientData[fds[i].fd] = clientData[fds[userCounter].fd];
//                 close(fds[i].fd);
//                 fds[i] = fds[userCounter];
//                 --i;
//                 --userCounter;
//                 std::printf("a client left\n");
//             } else if (fds[i].revents & POLLIN) {
//                 int connectFD = fds[i].fd;
//                 std::memset(clientData[connectFD].buffer, '\0', getBufferSize());
//                 ret = recv(connectFD, clientData[connectFD].buffer, getBufferSize() - 1, 0);
//                 std::printf("get %d bytes of client data %s from %d\n", ret, clientData[connectFD].buffer, connectFD);
//                 if (ret < 0) {
//                     // 如果读操作出错, 则关闭连接
//                     if (errno != EAGAIN) {
//                         close(connectFD);
//                         clientData[fds[i].fd] = clientData[fds[userCounter].fd];
//                         fds[i] = fds[userCounter];
//                         --i;
//                         --userCounter;
//                     }
//                 } else if (ret == 0) {

//                 } else {
//                     for (int j = 0; j <= userCounter; ++j) {
//                         if (fds[j].fd == connectFD) {
//                             continue;
//                         }
//                         fds[j].events |= ~POLLIN;
//                         fds[j].events |= POLLOUT;
//                         clientData[fds[j].fd].writeBuffer = clientData[connectFD].buffer;
//                     }
//                 }
//             } else if (fds[i].revents & POLLOUT) {
//                 int connectFD = fds[i].fd;
//                 if (!clientData[connectFD].writeBuffer) {
//                     continue;
//                 }
//                 ret = send(connectFD, clientData[connectFD].writeBuffer, std::strlen(clientData[connectFD].writeBuffer),
//                            0);
//                 clientData[connectFD].writeBuffer = NULL;
//                 // 写完数据后需要重新注册fds[i]上的可读事件
//                 fds[i].events |= ~POLLOUT;
//                 fds[i].events |= POLLIN;
//             }
//         }
//     }
//     delete[] clientData;
//     close(listenFD);

//     return 0;
// }








////
//// Created by LiuYou on 2021/5/1.
////
//#define _GNU_SOURCE 1
//
//#include <cygwin/in.h>
//#include <fcntl.h>
//#include <iostream>
//#include <libgen.h>
//#include <arpa/inet.h>
//#include <cassert>
//#include <sys/poll.h>
//#include <cstring>
//#include <sys/unistd.h>
//
//inline constexpr int getBufferSize() {
//    return 64;
//}
//
//struct ClientData {
//    sockaddr_in sockAddrIn;
//    char* writeBuffer;
//    char buffer[getBufferSize()];
//};
//
//int setNonBlocking(int fd) {
//    int oldOption = fcntl(fd, F_GETFL);
//    int newOption = oldOption | O_NONBLOCK;
//    fcntl(fd, F_SETFL, newOption);
//    return oldOption;
//}
//
//
//int main(int argc, char** argv) {
//    if (argc <= 2) {
//        std::cout << "usage: " << basename(argv[0]) << " IPAddress PortNumber" << std::endl;
//        return 1;
//    }
//    const char* ip = argv[1];
//    int port = std::atoi(argv[2]);
//
//    sockaddr_in sockAddrIn{
//            .sin_family=PF_INET,
//            .sin_port=htons(port),
//            {.s_addr=0}
//    };
//    inet_pton(AF_INET, ip, &sockAddrIn.sin_addr);
//
//    int listenFD = socket(PF_INET, SOCK_STREAM, 0);
//    assert(listenFD >= 0);
//
//    int ret = bind(listenFD, (sockaddr*) &sockAddrIn, sizeof(sockAddrIn));
//    assert(ret != -1);
//
//    ret = listen(listenFD, 5);
//    assert(ret != -1);
//
//    /*
//     * 创建users数组, 分配FD_LIMIT个ClientData对象。
//     * 可以预测: 每个可能的socket连接都可以获得一个这样的对象, 并且socket的值可以直接用来索引(作为数组下标)socket连接对应的ClientData对象,
//     * 这是将socket和客户数据关联的简单而高效的方式。
//     */
//    int FD_LIMIT = 65535;
//    ClientData* clientData = new ClientData[FD_LIMIT];
//    // 尽管我们分配了足够多的ClientData对象, 但为了提高poll的性能, 仍然有必要限制用户的数量。
//    int USER_LIMIT = 5;
//    pollfd fds[USER_LIMIT + 1];
//    for (int i = 1; i < USER_LIMIT; ++i) {
//        fds[i].fd = -1;
//        fds[i].events = 0;
//    }
//    fds[0].fd = listenFD;
//    fds[0].events = POLLIN | POLLERR;
//    fds[0].revents = 0;
//    int userCounter = 0;
//    for (;;) {
//        ret = poll(fds, userCounter + 1, -1);
//        if (ret < 0) {
//            std::printf("poll failure\n");
//            break;
//        }
//        for (int i = 0; i < userCounter + 1; ++i) {
//            if ((fds[i].fd == listenFD) && (fds[i].revents & POLLIN)) {
//                sockaddr_in clientSockAddrIn;
//                socklen_t clientSockAddrInLength = sizeof(clientSockAddrIn);
//                int connectFD = accept(listenFD, (sockaddr*) &clientSockAddrIn, &clientSockAddrInLength);
//                if (connectFD < 0) {
//                    std::printf("errno is: %d\n", errno);
//                    continue;
//                }
//                // 如果请求太多, 则关闭新到的连接
//                if (userCounter >= USER_LIMIT) {
//                    const char* info = "too many users\n";
//                    std::printf("%s", info);
//                    send(connectFD, info, std::strlen(info), 0);
//                    close(connectFD);
//                    continue;
//                }
//                /*
//                 * 对于新的连接, 同时修改fds和clientData数组。前文已经提到, clientData[connectFD]对应于
//                 * 新连接文件描述符connectFD的客户数据
//                 */
//                ++userCounter;
//                clientData[connectFD].sockAddrIn = clientSockAddrIn;
//                setNonBlocking(connectFD);
//                fds[userCounter].fd = connectFD;
//                fds[userCounter].events = POLLIN | POLLRDHUP | POLLERR;
//                fds[userCounter].revents = 0;
//                std::printf("comes a new user, now have %d users\n", userCounter);
//            } else if (fds[i].revents & POLLERR) {
//                std::printf("get an error from %d\n", fds[i].fd);
//                char errors[100];
//                std::memset(errors, '\0', 100);
//                socklen_t length = sizeof(errors);
//                if (getsockopt(fds[i].fd, SOL_SOCKET, SO_ERROR, &errors, &length) < 0) {
//                    std::printf("get socket option failed\n");
//                }
//                continue;
//            } else if (fds[i].revents & POLLRDHUP) {
//                // 如果客户端关闭连接, 则服务器也关闭对应的连接, 并将用户总数减1
//                clientData[fds[i].fd] = clientData[fds[userCounter].fd];
//                close(fds[i].fd);
//                fds[i] = fds[userCounter];
//                --i;
//                --userCounter;
//                std::printf("a client left\n");
//            } else if (fds[i].revents & POLLIN) {
//                int connectFD = fds[i].fd;
//                std::memset(clientData[connectFD].buffer, '\0', getBufferSize());
//                ret = recv(connectFD, clientData[connectFD].buffer, getBufferSize() - 1, 0);
//                std::printf("get %d bytes of client data %s from %d\n", ret, clientData[connectFD].buffer, connectFD);
//                if (ret < 0) {
//                    // 如果读操作出错, 则关闭连接
//                    if (errno != EAGAIN) {
//                        close(connectFD);
//                        clientData[fds[i].fd] = clientData[fds[userCounter].fd];
//                        fds[i] = fds[userCounter];
//                        --i;
//                        --userCounter;
//                    }
//                } else if (ret == 0) {
//
//                } else {
//                    for (int j = 0; j < = userCounter; ++j) {
//                        if (fds[j].fd == connectFD) {
//                            continue;
//                        }
//                        fds[j].events |= ~POLLIN;
//                        fds[j].events |= POLLOUT;
//                        clientData[fds[j].fd].writeBuffer = clientData[connectFD].buffer;
//                    }
//                }
//            } else if (fds[i].revents & POLLOUT) {
//                int connectFD = fds[i].fd;
//                if (!clientData[connectFD].writeBuffer) {
//                    continue;
//                }
//                ret = send(connectFD, clientData[connectFD].writeBuffer, std::strlen(clientData[connectFD].writeBuffer),
//                           0);
//                clientData[connectFD].writeBuffer = NULL;
//                // 写完数据后需要重新注册fds[i]上的可读事件
//                fds[i].events |= ~POLLOUT;
//                fds[i].events |= POLLIN;
//            }
//        }
//    }
//    delete[] clientData;
//    close(listenFD);
//
//    return 0;
//}